#pragma once
//共享内存是进程通信中速度最快的，映射之后直接被对方看到
//不需要进行系统调用，因为共享区属于内存空间，而需要系统调用的是文件内和缓冲区
//——动态库加载到内存让进程看到的原理，只不过它采取的是mmap的方式而不是systemV的方式
//缺点：没有所谓的同步机制！！（同步机制类似于管道的写端不写读端就不读）
//而且可能会导致语句中断，出现数据不一致
//总结：共享内存没有保护机制（对共享内存数据的保护），但这也是共享内存快的原因
//共享内存在创建过程中，必须是4kb的整数倍，向上取整

#include <iostream>
#include <string>
#include <sys/ipc.h>
#include <unistd.h>
#include <string>
#include <sys/types.h>
#include <sys/shm.h>

#define GDEFAULTNUM -1
#define GSIZE 4096
#define ERR_EXIT(m) do{perror(m);} while(0)
#define CREATER "creater"
#define USER "user"

const int GMODE = 0666;
const int projid = 0x67;//默认projid
const std::string pathname = ".";//默认创建路径

class Shm{
private:
    void CreateHelper(int flg)//这里的就是传递位信息
    {
        //key+大小+创建选项——创建且若已有则出错，保证共享内存一定是空的
        _shmid = shmget(_key, _size, flg);//Linux下一切皆文件
        if(_shmid < 0)
        {
            ERR_EXIT("shmget");
            return;
        }
        printf("the _shmid is %d\n", _shmid);
        return;
    }

    void Create()
    {
        CreateHelper(IPC_CREAT|IPC_EXCL|GMODE);
    }
    
    void Get()
    {
        CreateHelper(IPC_CREAT|GMODE);
    }

    void Attach()
    {
        //void* shmat(_shmid, const char* addr, int *flg)
        //挂载的起始地址空间的地址  要挂载的地址    选项
        _start_mem = shmat(_shmid, nullptr, 0);//既可读又可写，都采取了默认选项
        //权限具有问题，
        if((long long)_start_mem < 0)
        {
            ERR_EXIT("Attach");
        }
        printf("attach success\n");
    }

    void Detach()
    {
        int n = Detach(_start_mem);
        if(n == 0)
        {
            std::cout << "detach sucess" << std::endl;
        }
    }
    
    void Attr()
    {
        struct shmid_ds ds;//输出型参数
        int n = shmctl(_shmid, IPC_STAT, &ds);
        printf("shm_segsz: 0x%ld", ds.shm_segsz);
    }

    void Destroy()
    {
        //文件是一旦结束了，文件就会被关闭，文件描述符表、进程控制块等都会被释放
        //而共享内存的特点是生命周期随内核，不像文件一样随进程。
        //共享内存即使创建它的进程退出，IPC资源也不会被删除
        //查看共享内存：ipcs -m，删除共享内存：ipcrm -m shmid
        //这里不能使用key，因为key是给操作系统使用的，用来给内核区分唯一性
        if(_shmid == GDEFAULTNUM)
        {
            return;
        }
        //IPC_RMID记录将被摧毁的共享内存
        int n = shmctl(_shmid, IPC_RMID, nullptr);
        if(n < 0)
        {
            ERR_EXIT("shmctl");
            return;
        }
        else
        {
            printf("shmctl delete shm: %d success!\n", _shmid);
        }
    }
public:
    Shm(const std::string& pathname, int projid, const std::string& usertype) 
        : _shmid(GDEFAULTNUM)
        , _size(GSIZE)
        , _start_mem(nullptr)
        , _usertype(usertype)
    {
        //路径+id——构建一个key值
        _key = ftok(pathname.c_str(), projid);//projid = 0x66
        if(_key < 0)
        {
            ERR_EXIT("ftok");
            return;
        }
        printf("the key is 0x%x\n", _key);
        if(_usertype == CREATER)
            Create();
        else if(_usertype == USER)
            Get();
        Attach();
    }

    void* VirtualAddr()
    {
        printf("VirtualAddress is %p\n", _start_mem);
        return _start_mem;
    }

    int size()
    {
        return _size;
    }

    ~Shm()
    {
        Detach();//去关联
        if(_usertype == CREATER)
            Destroy();
    }

private:
    int _shmid;//共享内存标识符供用户使用，而ftok产生的是供操作系统使用的
    int _size;//大小
    key_t _key;
    void* _start_mem;//共享内存起始地址
    std::string _usertype;//用户类型
};
